﻿/*
 * Created by SharpDevelop.
 * User: MyWife
 * Date: 5/25/2010
 * Time: 11:26 PM
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */

using System;
using System.Collections;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Text;
using System.Xml;
using System.Xml.XPath;
using Microsoft.Win32;

namespace TortoiseSVNContextMenu
{
	/// <summary>
	/// Description of TortoiseSVN.
	/// </summary>
	[ Guid("B641AF9A-A0FF-447C-BB6C-919D4100847C"), ComVisible(true)]
	public class Handler: IShellExtInit, IContextMenu
	{
		private const String THIS_GUID = @"B641AF9A-A0FF-447C-BB6C-919D4100847C";
		private const String THIS_NAME = "TortoiseSVN";

		private const String XML_FILE_NAME = ".smart-menu";

		private const int S_OK = 0;
		private const int S_FALSE = 1;


		#region Data Members
		protected IDataObject m_dataObject = null;
		protected uint m_hDrop = 0;
		protected HybridDictionary m_commands = new HybridDictionary();
		#endregion Data Members

		public Handler()
		{
			Debug.WriteLine( "SmartContextMenu.Handler.ctor" );
		}

		#region IContextMenu
		private class CommandInfo
		{
			public String Exe;
			public String Arg;
			
			public CommandInfo(String exe, String arg)
			{
				Exe = exe;
				Arg = arg;
			}
		}

		private static void Run_( String cmd, String arg )
		{
			using ( Process p = new Process() )
			{

				p.StartInfo.FileName = cmd;
				p.StartInfo.Arguments = arg;

				p.Start();
			}
		}

		int	IContextMenu.QueryContextMenu(HMenu hMenu, int iMenu, int idCmdFirst, int idCmdLast, CMF uFlags)
		{
			//Debug.WriteLine( "SmartContextMenu.Handler.QueryContextMenu" );

			if ( ( uFlags & CMF.CMF_DEFAULTONLY ) != 0 )
				return 0;

			// Get the file name to work with
			StringBuilder sb = new StringBuilder(1024);
			Helpers.DragQueryFile(m_hDrop, 0, sb, sb.Capacity + 1);
			String dirPath = sb.ToString();
                        
			if ( ! ( (uFlags & (CMF.CMF_VERBSONLY|CMF.CMF_NOVERBS)) == 0 || (uFlags & CMF.CMF_EXPLORE) != 0) )
				return 0;
			
			int id = 0;
			string app = Path.Combine( Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) , "TortoiseSVN\\TortoiseProc.exe" );
			
            
            #region Add submenu item
            string[] nonesvnmenu = new string[] {
                "SVN Checkout...", 				"checkout", 	"path;end",
                "Repo-browser", 				"repobrowser", 	"path;end",
                "", "","",
                "Add", 							"add",			"path;end",
                "Export...", 					"export", 		"path;end",
                "Create repository here...", 	"repocreate", 	"path;end",
                "Import...", 					"import", 		"path;end",
                "Add to Ignore list...",		"ignore",		"path;end",
                "", "","",
                "Help", 						"help", 		"",
                "Settings", 					"settings", 	"",
                "About", 						"about", 		""
            };

            string[] svnmenu = new string[] { 
                "SVN Update", 							"update",		"path;end",
                "SVN Commit", 							"commit",		"path;end",
                "", "","",
                "Add", 									"add",			"path;end",
                "Delete", 								"remove",		"path;end",                
                "Revert...", 							"revert",		"path;end",
                "Update to revision...", 				"update",		"path;rev;end",
                "Different", 							"diff",			"path;end",
                "Rename",	 							"rename",		"path;end",
                "Clean up", 							"cleanup",		"path;end",
                "", "","",                
                "Show log", 							"log",			"path;end",
                "Branch/tag", 							"copy",			"path;end",
                "Switch", 								"switch",		"path;end",                
                "Merge", 								"merge",		"path;end",
                "Export", 								"export",		"path;end",                
                "Relocate", 							"relocate",		"path;end",
                "Repo-browser", 						"repobrowser", 	"path;end",                
                "Add to Ignore list...",				"ignore",		"path;end",
                //"Properties", 						"",				"path",
                "", "","",
                "Help", 								"help",			"",
                "Settings", 							"settings",		"",
                "About", 								"about",		""
            };

            HMenu subMenu = Helpers.CreatePopupMenu();
            string[] currentMenu = Directory.Exists( Path.Combine(dirPath,".svn") ) ? svnmenu :nonesvnmenu ;
            for (int i = 0; i < currentMenu.Length; i += 3)
            {
                string submenuName  = currentMenu[i];                
                string args			= string.Format( "/command:{0}", currentMenu[i+1] );
                string[] prefixes   = currentMenu[i+2].Split(';');
                foreach( string prefix in prefixes ){
                	switch( prefix ){
                		case "path": args += string.Format( " /path:\"{0}\"", dirPath); break;
                		case "rev":  args += " /rev"; break;
                		case "end":  args += " /notempfile /closeonend"; break;
                		default:     args += " "+prefix; break;
                	}
                }                
                m_commands.Add(id, new CommandInfo( "\"" + app + "\"" , args));
                
                if(submenuName == "" )
                    Helpers.AppendMenu(subMenu, MFMENU.MF_BYPOSITION | MFMENU.MF_SEPARATOR, new IntPtr(idCmdFirst + (id++)), submenuName);
                else
                    Helpers.AppendMenu(subMenu, MFMENU.MF_STRING | MFMENU.MF_ENABLED, new IntPtr(idCmdFirst + (id++)), submenuName);
            }
            string menuName = "Tortoise SVN";
            Helpers.InsertMenu(hMenu, iMenu,MFMENU.MF_BYPOSITION | MFMENU.MF_POPUP | MFMENU.MF_ENABLED, subMenu.handle, menuName);
            #endregion

            
            return id;
		}		
		void IContextMenu.GetCommandString(int idCmd, uint uFlags, int pwReserved, StringBuilder commandString, int cchMax)
		{
			//Debug.WriteLine( "SmartContextMenu.Handler.GetCommandString" );

			// GCS.VERB is the language indep command
			// GCS.HELPTEXT is the help :-)
			switch((GCS)uFlags)
			{
				default:
					break;
			}
		}
		void IContextMenu.InvokeCommand(IntPtr pici)
		{
			Debug.WriteLine( "SmartContextMenu.Handler.InvokeCommand" );

			try
			{
				INVOKECOMMANDINFO ici = (INVOKECOMMANDINFO)Marshal.PtrToStructure(pici, typeof(INVOKECOMMANDINFO));

				int id = ici.verb;

				if ( m_commands.Contains( id ) )
				{
					CommandInfo ci = (CommandInfo) m_commands[ id ];
					Run_( ci.Exe, ci.Arg );
				}
			}
			catch(Exception)
			{
			}
        }


        #region add more
        void AddMenuItem(HMenu hMenu, string text, int id, uint position)
        {
            MENUITEMINFO mii = new MENUITEMINFO();
            mii.cbSize = 48;
            mii.fMask = (uint)MIIM.ID | (uint)MIIM.TYPE | (uint)MIIM.STATE;
            mii.wID = id;
            mii.fType = (uint)MF.STRING;
            mii.dwTypeData = text;
            mii.fState = (uint)MF.ENABLED;
            Helpers.InsertMenuItem(hMenu, position, 1, ref mii);
        }

        int PopulateMenu(HMenu hMenu, int id)
        {
            // Grab some info about the file
            bool done = ProcessBatchResults();

            if (done)
            {
                AddMenuItem(hMenu, "Reschedule", id, 0);
                AddMenuItem(hMenu, "Retry Now", ++id, 1);
                AddMenuItem(hMenu, "Cancel", ++id, 2);
            }
            else
            {
                // Uses 100-baed values to distinguish on the "done" status
                AddMenuItem(hMenu, "Commit", 100 + id, 0);
                AddMenuItem(hMenu, "Rollback", 100 + (++id), 1);
            }

            return id++;
        }

        bool ProcessBatchResults()
        {
            //FileInfo fi = new FileInfo(m_fileName);
            //return (fi.Length > 0);
            return true;
        }
        #endregion

        #endregion IContextMenu

        #region IShellExtInit
        int	IShellExtInit.Initialize(IntPtr pidlFolder, IntPtr lpdobj, uint hKeyProgID)
		{
			Debug.WriteLine( "SmartContextMenu.Handler.Initialize" );

			try
			{
				// save the information about the selection
				m_dataObject = null;
				if (lpdobj != (IntPtr)0)
				{
					m_dataObject = (IDataObject)Marshal.GetObjectForIUnknown(lpdobj);
					FORMATETC fmt = new FORMATETC();
					fmt.cfFormat = CLIPFORMAT.CF_HDROP;
					fmt.ptd		 = 0;
					fmt.dwAspect = DVASPECT.DVASPECT_CONTENT;
					fmt.lindex	 = -1;
					fmt.tymed	 = TYMED.TYMED_HGLOBAL;
					STGMEDIUM medium = new STGMEDIUM();
					m_dataObject.GetData(ref fmt, ref medium);
					m_hDrop = medium.hGlobal;
				}
			}
			catch(Exception)
			{
			}

			return 0;
		}
		#endregion IShellExtInit

		#region Registration
		const int SHCNE_ASSOCCHANGED=0x08000000;

		[DllImport("Shell32.dll")]
		static extern void SHChangeNotify(int wEventId, uint uFlags, IntPtr dwItem1, IntPtr dwItem2);

		private static void RegisterHandlerUsingKey_(RegistryKey rk)
		{
			using ( rk )
			{
				RegistryKey rkTHIS = rk.OpenSubKey( THIS_NAME );
				if (rkTHIS == null)
					rkTHIS = rk.CreateSubKey( THIS_NAME );

				using ( rkTHIS )
				{
					rkTHIS.SetValue( "", "{" + THIS_GUID + "}");
				}
			}
		}
		[System.Runtime.InteropServices.ComRegisterFunctionAttribute()]
		static void RegisterServer(String str1)
		{
			try
			{
				RegisterHandlerUsingKey_(
					Registry.ClassesRoot.OpenSubKey( @"Directory\shellex\ContextMenuHandlers", true) );

				// set as an approved shell extension so its use does
				// not require local admin rights
				using ( RegistryKey rkApproved =
							Registry.LocalMachine.OpenSubKey(
								@"Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved", true) )
				{
					rkApproved.SetValue( "{" + THIS_GUID + "}", "Smart Context Menu Shell Extension");
				}

				// tell explorer to refresh
				SHChangeNotify(SHCNE_ASSOCCHANGED, 0, IntPtr.Zero, IntPtr.Zero);
				Debug.WriteLine( THIS_NAME + " registered successfuly" );
			}
			catch (Exception e)
			{
				Debug.WriteLine(e.ToString());
			}
		}
		[System.Runtime.InteropServices.ComUnregisterFunctionAttribute()]
		static void UnregisterServer(String str1)
		{
			try
			{
				using ( RegistryKey rk =
							Registry.ClassesRoot.OpenSubKey(@"Directory\shellex\ContextMenuHandlers", true) )
				{
					rk.DeleteSubKey( THIS_NAME, false );
				}

				// set as an approved shell extension so its use does
				// not require local admin rights
				using ( RegistryKey rkApproved =
							Registry.LocalMachine.OpenSubKey(
								@"Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved", true) )
				{
					rkApproved.DeleteValue( "{" + THIS_GUID + "}", false);
				}

				// tell explorer to refresh
				SHChangeNotify(SHCNE_ASSOCCHANGED, 0, IntPtr.Zero, IntPtr.Zero);
				Debug.WriteLine( THIS_NAME + " un-registered successfuly" );
			}
			catch (Exception e)
			{
				Debug.WriteLine(e.ToString());
			}
		}
		#endregion Registration
	}
}
